<?php
/**
 * 文件提供导出表格的功能，使用者只需要正确的引入 PHPExcel 类库即可
 *
 * 此文件的文件名 类名 命名空间 都可以自定义，
 *
 * 导出表格功能的数据写入及参数设置包括 ：
 *    1、单、多行写入 ，跨行、列写入，单个单元格设置，文本、图片、超链接
 *    2、定义默认表格属性，定义 sheet
 *    3、表格内容的样式设置，单个单元格的样式设置
 *    4、跨越行列不写入操作，表格指针移动
 */


class ExportExcel{

    protected $obj=null;            // 引入的类库的实例
    protected $current_row_arr=[];      // 当前行数组   横向

    protected $current_row=1;         // 当前行   横向
    protected $current_sheet=0;     // 当前页
    protected $sheet_num = 1;         // 当前工作表总数
    protected $sheet_title_arr=[];    // 标题 索引对照表 仅函数内使用

    // 定义 常量

    /* Horizontal alignment styles   复制于引入类库 ，以第三方类库为准 */
    const HORIZONTAL_GENERAL           = 'general';
    const HORIZONTAL_LEFT              = 'left';
    const HORIZONTAL_RIGHT             = 'right';
    const HORIZONTAL_CENTER            = 'center';
    const HORIZONTAL_CENTER_CONTINUOUS = 'centerContinuous';
    const HORIZONTAL_JUSTIFY           = 'justify';
    const HORIZONTAL_FILL              = 'fill';
    const HORIZONTAL_DISTRIBUTED       = 'distributed';        // Excel2007 only

    /* Vertical alignment styles 复制于引入类库 ，以第三方类库为准 */
    const VERTICAL_BOTTOM      = 'bottom';
    const VERTICAL_TOP         = 'top';
    const VERTICAL_CENTER      = 'center';
    const VERTICAL_JUSTIFY     = 'justify';
    const VERTICAL_DISTRIBUTED = 'distributed';        // Excel2007 only

    /* 数据类型  */

    const TYPE_STRING       =   'string';
    const TYPE_IMG          =   'img';
    const TYPE_LINK         =   'link';
  //  const TYPE_IMG_LINK     =   'img_link';

    //------------------

    /**
     * 获取 PHPExcel 类库实例 ，需要正确的引入类库，可以在本方法中引入，也可以在外部引入
     *  全局统一使用 ，不用担心流程问题
     * @return \PHPExcel|null
     */

    protected function getObj(){
        if(!$this->obj){
            //  Classes 文件夹是 PHPExcel 类库的文件夹 ，PHPExcel.php  是它的入口文件，里面注册了 自动加载函数 spl_autoload_register ，
            //  使用类库相关的数据时会自动加载  ， 你如果有了自己的注册函数引入此文件，则可以不手动引入此文件
            require_once __DIR__.'/Classes/PHPExcel.php';
            $this->obj=new \PHPExcel();   // 正确的实例化
        }
        return $this->obj;
    }

    /**
     * 获取 Excel 实例 ，便于独立设置其他属性
     * @return null
     */
    public function getOriExcelObj(){
        return $this->obj;
    }

    /**
     * 设置 表格属性
     * PhpExcel constructor
     * @param string $title 标题
     * @param string $creator  作者
     * @param string $last_modified 最后修改
     * @param string $subject 主题
     * @param string $description 备注
     * @param string $key_words 关键字
     * @param string $category  文档分类
     * @throws \PHPExcel_Exception
     */
    public function __construct($title='sheet',$creator='',$last_modified='',$subject='',$description='',$key_words='',$category=''){

        $this->getObj();// 实例化

        $this->obj->getProperties()
            ->setCreator($creator)
            ->setLastModifiedBy($last_modified)
            ->setTitle($title)
            ->setSubject($subject)
            ->setDescription($description)
            ->setKeywords($key_words)
            ->setCategory($category);

        //$this->current_col_arr[$this->current_sheet]=$this->current_col;
        $this->current_row_arr[$this->current_sheet]=$this->current_row;
        $this->sheet_title_arr[$this->encodeStr($title)]=$this->current_sheet;
        return $this;
    }

    /**
     * 设置表格 默认属性
     * @param array $style
     * @return $this
     */
    public function setDefStyle($style=[]){
        if(empty($style)){
            return $this;
        }
        $objSheet=$this->obj->getActiveSheet();

        isset($style['font_size'])         and $objSheet->getDefaultStyle()->getFont()->setSize($style['font_size']);       //设置默认字体大小
        isset($style['font_name'])         and $objSheet->getDefaultStyle()->getFont()->setName($style['font_name']);       //设置默认字体格式
        isset($style['row_height'])        and $objSheet->getDefaultRowDimension()->setRowHeight($style['row_height']);     //设置默认行高
        isset($style['vertical'])          and $objSheet->getDefaultStyle()->getAlignment()->setVertical($style['vertical']);//设置excel文件默认垂直方向
        isset($style['horizontal'])        and $objSheet->getDefaultStyle()->getAlignment()->setHorizontal($style['horizontal']);//设置excel文件默认水平方向

        return $this;
    }

    /**
     * 选择当前工作表 ,不存在时 创建
     * @param string $title 工作表标题
     * @return $this|bool
     */
    public function chooseShell($title=''){
        if(isset($this->sheet_title_arr[$this->encodeStr($title)])){
            $this->current_sheet=$this->sheet_title_arr[$this->encodeStr($title)];
            $this->obj->setActiveSheetIndex($this->current_sheet);
        }else{
             $this->createShell($title);
        }

        $this->current_row=$this->current_row_arr[$this->current_sheet];
        return $this;
    }

    /**
     * 设置 当前行
     * @param $num 数字
     * @param bool $span 是否跨越
     *            false  表示不跨越 ，设置当前行为   $num
     *            true   表示跨越  ， 从当前行跨越 $num  到指定行
     * @return $this
     */
    public function setCurrentRow($num ,$span=false){
        if($span){
            $_temp=$this->current_row+$num;
        }else{
            $_temp=$num;
        }
        if($_temp<1){
            $_temp=1;
        }
        $this->current_row_arr[$this->current_sheet]=$_temp;
        $this->current_row=$_temp;
        return $this;
    }

    /**
     * 获取 当前行
     * @return int
     */
    public function getCurrentRow(){
        return $this->current_row;
    }

    /**
     * 设置当前工作表的 标题
     * @param null $title
     * @return $this|bool|ExportExcel
     */
    public function setSheetTitle($title=null){
        $ori_title=$this->obj->getActiveSheet()->getTitle();
        if($ori_title==$title){
            return $this;
        }

        if(isset($this->sheet_title_arr[$this->encodeStr($title)])){
            return $this->chooseShell($title);
        }

        $this->obj->getActiveSheet()->setTitle($title);
        return $this;
    }

    /**
     * 获取当前页  标题
     * @return int
     */
    public function getSheetTitle(){
        return $this->obj->getActiveSheet()->getTitle();
    }

    /**
     * 获取当前页  索引
     * @return int
     */
    public function getSheetIndex(){
        return $this->current_sheet;
    }

    /**
     * 设置单元格 内容
     * @param $val 内容
     * @param string $cell 索引
     * @return PHPExcel_Cell|PHPExcel_Worksheet
     * @throws PHPExcel_Exception
     */
    public  function setCell($val,$cell='A1',$span=false){
        if($span){
            $this->obj->getActiveSheet()->mergeCells($cell);
            $_temp=explode(':',$cell);
            $cell=$_temp[0];
        }
        return $this->_setCell($val,$cell);
    }

    /**
     * 设置一行数据
     * @param $data 一 二 维数组
     * @return ExportExcel
     * @throws PHPExcel_Exception
     */
    public function setALineData($data){
        return $this->_setALineData($data);
    }

    /**
     * 设置多行数据
     * @param $data 二 三 维数组
     * @return $this
     * @throws PHPExcel_Exception
     */
    public function setMultilineData($data){
        foreach ($data as $k=>$v){
            $this->_setALineData($v);
        }
        return $this;
    }

    /**
     * 导出 行为 ， 与 getObj  一样，需要正确的实例化
     * @param string $filename 文件名
     * @throws \PHPExcel_Reader_Exception
     * @throws \PHPExcel_Writer_Exception
     */
    public function write($filename='excel',$is_file=false){
        if($is_file){
            $objWriter=\PHPExcel_IOFactory::createWriter($this->obj,'Excel2007');   // 正确的实例化
            return $objWriter->save($filename.'.xlsx');
        }else{
            $this->setHeader($filename);
            $objWriter=\PHPExcel_IOFactory::createWriter($this->obj,'Excel2007');   // 正确的实例化
            $objWriter->save('php://output');
        }
    }

    /**
     * 设置一行数据
     * @param $data  一 二维数组  一维数组表示直接写入
     *  二维数组则是会设置属性   [  [ 'col_pass_num'=>1, 'col_span_num'=>2  ] , [] ]  ,其他设置参数 请参照 _setCell()
     * @return $this
     * @throws PHPExcel_Exception
     */
    protected function _setALineData($data){
        $current_col = 'A';   // 当前列

        foreach ($data as $v) {
            $cell = "{$current_col}{$this->current_row}";

            if (is_array($v)) {
                // 如果不是从第一列写入 ，找到 写入位置
                if (isset($v['col_pass_num'])) {
                    $current_col = $this->colPass($current_col, $v['col_pass_num']);
                    unset($v['col_pass_num']);
                    $cell = "{$current_col}{$this->current_row}";
                }

                // 如果需要合并单元格
                if (isset($v['col_span_num'])) {
                    $s = "{$current_col}{$this->current_row}";
                    $current_col = $this->colspan($current_col, $v['col_span_num']);
                    $e = "{$current_col}{$this->current_row}";
                    $this->obj->getActiveSheet()->mergeCells("{$s}:{$e}");
                    unset($v['col_span_num']);
                }
            }

            $this->_setCell($v, $cell);
            $current_col++;
        }

        $this->current_row_arr[$this->current_sheet]= $this->current_row++;
        return $this;
    }

    /**
     * 设置单元内容  字符串 则直接写入 ，数组，表示有格式设置
     * @param string $cell 单元格定位
     * @param array $val 内容  value  size  color gb
     * @return \PHPExcel_Cell|\PHPExcel_Worksheet
     * @throws \PHPExcel_Exception
     *
     * val 设置
     * value 必须，单元格内容
     * type  类型 字符串 图片 链接
     * font_size 字体大小
     * bold 粗体
     * horizontal 水平样式  general  left right center centerContinuous justify fill distributed
     * color 字体颜色
     * bg  背景色
     */
    protected function _setCell($val,$cell='A1'){
        if(!is_array($val)){
            $this->obj->setActiveSheetIndex($this->current_sheet)->setCellValue($cell, $val);
            return $this;
        }
        if(isset($val['type'])){
            if($val['type']==self::TYPE_IMG){
                return $this->_setCellImg($val,$cell);
            }elseif ($val['type']==self::TYPE_IMG_LINK){

            }
        }

        isset($val['value'])        and     $this->obj->getActiveSheet()->setCellValue($cell, $val['value']);//$this->obj->setActiveSheetIndex($this->current_sheet)->setCellValue($cell, $val['value']);
        isset($val['font_size'])    and     $this->obj->getActiveSheet()->getStyle($cell)->getFont()->setSize($val['font_size']);
        isset($val['font_name'])    and     $this->obj->getActiveSheet()->getStyle($cell)->getFont()->setName($val['font_name']);
        isset($val['bold'])         and     $this->obj->getActiveSheet()->getStyle($cell)->getFont()->setBold(true);
        isset($val['horizontal'])   and     $this->obj->getActiveSheet()->getStyle($cell)->getAlignment()->setHorizontal($val['horizontal']);
        isset($val['vertical'])     and     $this->obj->getActiveSheet()->getStyle($cell)->getAlignment()->setHorizontal($val['vertical']);
        isset($val['color'])        and     $this->obj->getActiveSheet()->getStyle($cell)->getFont()->getColor()->setARGB($val['color']);
        isset($val['bg'])           and
            (   $this->obj->getActiveSheet()->getStyle($cell)->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)
            and $this->getObj()->getActiveSheet()->getStyle($cell)->getFill()->getStartColor()->setARGB($val['bg']) );

        isset($val['border_color'])         and     $this->obj->getActiveSheet()->getStyle($cell)->getBorders()->getLeft()->getColor()->setARGB($val['border_color']);
        isset($val['url'])                  and     $this->obj->getActiveSheet()->getCell($cell)->getHyperlink()->setUrl($val['url']);
        isset($val['url_tooltip'])          and     $this->obj->getActiveSheet()->getCell($cell)->getHyperlink()->setTooltip($val['url_tooltip']);

        return $this;




    }

    /**
     * 设置 单元格为图片
     * @param $val
     * @param $cell
     * @return $this
     * @throws PHPExcel_Exception
     */
    protected function _setCellImg($val,$cell){
        $objDrawing = new PHPExcel_Worksheet_Drawing();
        $objDrawing->setCoordinates($cell);

        isset($val['img_name'])             and        $objDrawing->setName($val['img_name']);
        isset($val['img_description'])      and        $objDrawing->setDescription($val['img_description']);
        isset($val['img_src'])              and        $objDrawing->setPath($val['img_src']);
        isset($val['img_proportional'])     and        $objDrawing ->setResizeProportional($val['img_proportional']);
        isset($val['img_height'])           and        $objDrawing->setHeight($val['img_height']);

        isset($val['img_offset_x'])         and         $objDrawing->setOffsetX($val['img_offset_x']);
        isset($val['img_offset_y'])         and         $objDrawing->setOffsetY($val['img_offset_y']);
        isset($val['img_rotation'])         and         $objDrawing->setRotation($val['img_rotation']);

        $objDrawing->setWorksheet($this->obj->getActiveSheet());

        return $this;
    }


    /**
     * 获取合并列之后的列数
     * @param string $start_column
     * @param int $colspan_num
     * @return string
     */
    protected function colspan( $start_column='A', $colspan_num=0){
        if($colspan_num>1){
            for($i=1;$i<$colspan_num;$i++){
                $start_column++;
            }
        }
        return $start_column;
    }

    /**
     * 获取跨列之后的列数
     * @param string $start_column
     * @param int $colspan_num
     * @return string
     */
    protected function colPass( $start_column='A', $colspan_num=0){
        if($colspan_num>0){
            for($i=0;$i<$colspan_num;$i++){
                $start_column++;
            }
        }
        return $start_column;
    }

    /**
     * 设置导出头
     * @param string $filename
     */
    protected function setHeader($filename='excel'){
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment;filename="'.$filename.'.xlsx"');
        header('Cache-Control: max-age=0');
        // If you're serving to IE 9, then the following may be needed
        header('Cache-Control: max-age=1');
        // If you're serving to IE over SSL, then the following may be needed
        header ('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
        header ('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); // always modified
        header ('Cache-Control: cache, must-revalidate'); // HTTP/1.1
        header ('Pragma: public'); // H
    }

    /**
     * 加密字符串
     * @param string $str
     * @return string
     */
    protected function encodeStr($str=''){
        return md5($str);
    }

    /**
     * 创建新 工作表
     * @param $title
     * @return $this|bool
     */
    protected function createShell($title){
        $this->current_sheet
            =$this->sheet_title_arr[$this->encodeStr($title)]
            =$this->sheet_num;
        $this->sheet_num++;

        $this->obj->createSheet( $this->current_sheet);

        $this->obj->setActiveSheetIndex($this->current_sheet);
        $this->obj->getActiveSheet()->setTitle($title);
        $this->current_row=$this->current_row_arr[$this->current_sheet]=1;

        return $this;
    }

}
